using System;
using System.Collections.Generic;
using System.Text;

using OOFluidFramework.Operators;
using OOFluidFramework.Utility;

namespace OOFluidFramework.Orders
{
    class GridArrangeOrderImpl : FluidOrderImpl, GridArrangeOrder
    {
        protected EulerianOperator[,] uGrid;
        protected EulerianOperator[,] vGrid;
        protected int gridWidth, gridHeight;
        protected double ssiz;

        protected Vector2 uGridOffset;
        protected Vector2 vGridOffset;

        public GridArrangeOrderImpl()
            : base()
        {
            ssiz = 0.0;            
        }
         
        public double StepSize
        {
            get {
                return ssiz;
            }
            set {
                //TODO: Add finer controls here
                ssiz = value;
                uGridOffset = new Vector2(ssiz / 2, 0);
                vGridOffset = new Vector2(0, ssiz / 2);
            }
        }

        public int SetGridSize(int Width, int Height)
        {
            gridWidth = Width;
            gridHeight = Height;
            uGrid = new EulerianOperator[Width, Height + 1];
            vGrid = new EulerianOperator[Width + 1, Height];
            //We use a MAC grid and thus need extra size of grids            
        }
       public Vector2 Register(EulerianOperator op, int xIndex, int yIndex, bool isUop)
       {
           //The Register method would place the indicated operator on the indicated grid,
           //if there was an operator there, it is discarded.
           switch (isUop)
           {
               case true : //we're dealing with an U-speed operator
                   uGrid[xIndex, yIndex] = op;
                   op.Location = FromGridtoWorldU(xIndex, yIndex);
                   break;
               case false:
                   vGrid[xIndex, yIndex] = op;
                   op.Location = FromGridtoWorldV(xIndex, yIndex);
                   break;
           }
           return new Vector2(op.Location);
       }
        protected Vector2 FromGridtoWorldU(int xIndex, int yIndex)
        {
            return new Vector2 ((xIndex + 0.5) * ssiz, yIndex * ssiz);
        }
        protected Vector2 FromGridtoWorldV(int xIndex, int yIndex)
        {
            return new Vector2(xIndex * ssiz, (yIndex + 0.5) * ssiz);
        }

        public override List<FluidOperator> Sieve(Delimiter del)
        {
            return Sieve(del);
        }
        public override List<FluidOperator> Sieve(Delimiter del)
        {
            switch (del.ReportType())
            {
                case (Delimiter.DelimiterType.Circle2D) :
                    return SieveCircleOperators((Delimiter2DCircle ) del);
                case (Delimiter.DelimiterType.Rectangle2D) :
                    return SieveRectangle2D((Delimiter2DRetangle ) del);
            }
        }
        protected List<FluidOperator> SieveCircleOperators(Delimiter2DCircle del)
        {
            int lowxIndex, lowyIndex, upxIndex, upyIndex;
            int i, j;           
            List<FluidOperator> toReturn;
            FromWorldtoGrid(new Vector2(del.Center.x - del.Radius, del.Center.y - del.Radius),
                out lowxIndex, out lowyIndex);
            if (lowxIndex < 0) lowxIndex = 0;
            if (lowyIndex < 0) lowyIndex = 0;
            FromWorldtoGrid(new Vector2(del.Center.x + del.Radius, del.Center.y + del.Radius),
                out upxIndex, out upyIndex);
            upxIndex++; upyIndex++;
            if (upxIndex > gridWidth) upxIndex = gridWidth;
            if (upyIndex > gridHeight) upyIndex = gridHeight;
            //We scan from grid cross point (lowxIndex, lowyIndex) to (upxIndex, upyIndex)
            //and see if any MAC speculators is in range
            //This part could be optimized if more advanced math is used
            //First is the u grid : from (xL,yL) to (xH-1,yH)
            for (i=lowxIndex;i<upxIndex;i++)
                for (j=lowyIndex;j<=upyIndex;j++)
                  if (uGrid[i, j].ReportDistanceP2(del.Center) < del.RadiusP2) toReturn.Add(uGrid[i, j]);
            //Second is the v grid : from (xL,yL) to (xH,yH-1)
            for (i = lowxIndex; i <= upxIndex; i++)
               for (j = lowyIndex; j < upyIndex; j++)
                  if (vGrid[i, j].ReportDistanceP2(del.Center) < del.RadiusP2) toReturn.Add(vGrid[i, j]);

          return toReturn;
        }
        protected List<FluidOperator> SieveRectangleOperators(Delimiter2DRetangle del)
        {
            int lowxIndex, lowyIndex, upxIndex, upyIndex;
            Vector2 TruncateError;
            List<FluidOperator> toReturn;
            int i, j;
            //We first scan the u grid
            TruncateError = FromWorldtoGrid(del.LowerLeft + uGridOffset, out lowxIndex, out lowyIndex);
            if (TruncateError.x > EPSLION) lowxIndex++;
            if (TruncateError.y > EPSLION) lowyIndex++;
            BoundIndex(out lowxIndex, out lowyIndex, true);            
            FromWorldtoGrid(del.UpperRight + uGridOffset, out upxIndex, out upyIndex);
            BoundIndex(upxIndex, upxIndex, true);
            //We scan through this cord to get out all operators
            for (i=lowxIndex;i<=upxIndex;i++)
                for (j=lowyIndex;j<=upyIndex;j++)
                    toReturn.Add(uGrid[i, j]);
            //Second we scan the V grid
            TruncateError = FromWorldtoGrid(del.LowerLeft + vGridOffset, out lowyIndex, out lowyIndex);
            if (TruncateError.x > EPSLION) lowxIndex++;
            if (TruncateError.y > EPSLION) lowyIndex++;
            BoundIndex(out lowxIndex, out lowyIndex, false);
            FromWorldtoGrid(del.UpperRight + vGridOffset, out upxIndex, out upyIndex);
            BoundIndex(out upxIndex, out upyIndex, false);
            for (i = lowxIndex; i <= upxIndex; i++)
                for (j = lowyIndex; j <= upyIndex; j++)
                    toReturn.Add(vGrid[i, j]);
            return toReturn;
        }

        protected Vector2 FromWorldtoGrid(Vector2 Location, out int xIndex, out int yIndex)
        {
            //This procedure returns the lower-left corner's grid index
            xIndex = Math.Truncate(Location.x / ssiz);
            yIndex = Math.Truncate(Location.y / ssiz);
            return new Vector2(Location.x - xIndex * ssiz, Location.y - yIndex * ssiz);
        }

        protected void BoundIndex(out int xIndex, out int yIndex, bool isUDirection)
        {
            if (xIndex < 0) xIndex = 0;
            if (yIndex < 0) yIndex = 0;
            if (isUDirection) //U direction means we have yIndex = Height
            {
                if (xIndex >= gridWidth) xIndex = gridWidth - 1;
                if (yIndex > gridHeight) yIndex = gridHeight;
            }
            else //V direction means we have xIndex = Width
            {
                if (xIndex > gridWidth) xIndex = gridWidth;
                if (yIndex >= gridHeight) yIndex = gridHeight;
            }

        }
        
    }

    interface GridArrangeOrder : FluidOrder
    {
        override List<EulerianOperator> Sieve(Delimiter dec);
        double StepSize
        {
            get;
            set;
        }
        int SetGridSize(int Width, int Height);
        vector2 Register(EulerianOperator op, int xIndex, int yIndex);
    }
}
